package com.school.controller;

import com.school.common.Result;
import com.school.common.ResultCode;
import com.school.entity.dto.LoginRequest;
import com.school.entity.dto.LoginResponse;
import com.school.exception.BusinessException;
import com.school.service.AuthService;
import com.school.util.JwtUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.util.HashMap;
import java.util.Map;

/**
 * 认证控制器
 * 
 * @author School Development Team
 * @version 1.0.0
 * @since 2024
 */
@Tag(name = "认证管理", description = "用户登录、登出、令牌管理等认证相关接口")
@RestController
@RequestMapping("/auth")
@Validated
public class AuthController {

    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);

    @Autowired
    private AuthService authService;

    @Autowired
    private JwtUtil jwtUtil;

    /**
     * 用户登录
     */
    @Operation(summary = "用户登录", description = "用户通过用户名和密码进行登录认证")
    @PostMapping("/login")
    public Result<LoginResponse> login(@Valid @RequestBody LoginRequest loginRequest, 
                                     HttpServletRequest request) {
        try {
            logger.info("用户登录请求: {}, IP: {}", loginRequest.getUsername(), getClientIp(request));
            
            LoginResponse loginResponse = authService.login(loginRequest);
            
            logger.info("用户登录成功: {}", loginRequest.getUsername());
            return Result.success("登录成功", loginResponse);
        } catch (BusinessException e) {
            logger.warn("用户登录失败: {}, 原因: {}", loginRequest.getUsername(), e.getMessage());
            return Result.<LoginResponse>error(e.getResultCode());
        } catch (Exception e) {
            logger.error("用户登录异常: {}, 错误: {}", loginRequest.getUsername(), e.getMessage(), e);
            return Result.<LoginResponse>error(ResultCode.SYSTEM_ERROR, "登录失败，请稍后重试");
        }
    }

    /**
     * 用户登出
     */
    @Operation(summary = "用户登出", description = "用户登出，使当前令牌失效")
    @PostMapping("/logout")
    public Result<Void> logout(HttpServletRequest request) {
        try {
            String token = extractToken(request);
            if (token == null) {
                return Result.<Void>error(ResultCode.TOKEN_INVALID, "令牌不存在");
            }

            String username = jwtUtil.getUsernameFromToken(token);
            boolean success = authService.logout(token);
            
            if (success) {
                logger.info("用户登出成功: {}", username);
                return Result.successVoid("登出成功");
            } else {
                return Result.<Void>error(ResultCode.ERROR, "登出失败");
            }
        } catch (Exception e) {
            logger.error("用户登出异常: {}", e.getMessage(), e);
            return Result.<Void>error(ResultCode.SYSTEM_ERROR, "登出失败，请稍后重试");
        }
    }

    /**
     * 刷新令牌
     */
    @Operation(summary = "刷新令牌", description = "使用刷新令牌获取新的访问令牌")
    @PostMapping("/refresh")
    public Result<LoginResponse> refreshToken(
            @Parameter(description = "刷新令牌", required = true)
            @NotBlank(message = "刷新令牌不能为空")
            @RequestParam String refreshToken) {
        try {
            LoginResponse loginResponse = authService.refreshToken(refreshToken);
            
            logger.info("令牌刷新成功: {}", loginResponse.getUserInfo().getUsername());
            return Result.success("令牌刷新成功", loginResponse);
        } catch (BusinessException e) {
            logger.warn("令牌刷新失败: {}", e.getMessage());
            return Result.<LoginResponse>error(e.getResultCode());
        } catch (Exception e) {
            logger.error("令牌刷新异常: {}", e.getMessage(), e);
            return Result.<LoginResponse>error(ResultCode.SYSTEM_ERROR, "令牌刷新失败，请稍后重试");
        }
    }

    /**
     * 验证令牌
     */
    @Operation(summary = "验证令牌", description = "验证访问令牌是否有效")
    @GetMapping("/validate")
    public Result<Map<String, Object>> validateToken(HttpServletRequest request) {
        try {
            String token = extractToken(request);
            if (token == null) {
                return Result.<Map<String, Object>>error(ResultCode.TOKEN_INVALID, "令牌不存在");
            }

            boolean isValid = authService.validateToken(token);
            
            Map<String, Object> result = new HashMap<>();
            result.put("valid", isValid);
            
            if (isValid) {
                result.put("userId", jwtUtil.getUserIdFromToken(token));
                result.put("username", jwtUtil.getUsernameFromToken(token));
                result.put("userType", jwtUtil.getUserTypeFromToken(token));
                result.put("expirationDate", jwtUtil.getExpirationDateFromToken(token));
            }
            
            return Result.success("令牌验证完成", result);
        } catch (Exception e) {
            logger.error("令牌验证异常: {}", e.getMessage(), e);
            return Result.<Map<String, Object>>error(ResultCode.SYSTEM_ERROR, "令牌验证失败");
        }
    }

    /**
     * 修改密码
     */
    @Operation(summary = "修改密码", description = "用户修改自己的密码")
    @PutMapping("/password")
    public Result<Void> changePassword(
            @Parameter(description = "旧密码", required = true)
            @NotBlank(message = "旧密码不能为空")
            @RequestParam String oldPassword,
            @Parameter(description = "新密码", required = true)
            @NotBlank(message = "新密码不能为空")
            @RequestParam String newPassword,
            HttpServletRequest request) {
        try {
            String token = extractToken(request);
            if (token == null) {
                return Result.<Void>error(ResultCode.TOKEN_INVALID, "令牌不存在");
            }

            if (!authService.validateToken(token)) {
                return Result.<Void>error(ResultCode.TOKEN_INVALID, "令牌无效");
            }

            String userId = jwtUtil.getUserIdFromToken(token);
            String username = jwtUtil.getUsernameFromToken(token);
            
            boolean success = authService.changePassword(userId, oldPassword, newPassword);
            
            if (success) {
                logger.info("用户密码修改成功: {}", username);
                return Result.successVoid("密码修改成功");
            } else {
                return Result.<Void>error(ResultCode.ERROR, "密码修改失败");
            }
        } catch (BusinessException e) {
            logger.warn("密码修改失败: {}", e.getMessage());
            return Result.<Void>error(e.getResultCode());
        } catch (Exception e) {
            logger.error("密码修改异常: {}", e.getMessage(), e);
            return Result.<Void>error(ResultCode.SYSTEM_ERROR, "密码修改失败，请稍后重试");
        }
    }

    /**
     * 重置密码（管理员功能）
     */
    @Operation(summary = "重置密码", description = "管理员重置用户密码")
    @PutMapping("/password/reset")
    public Result<Void> resetPassword(
            @Parameter(description = "用户名", required = true)
            @NotBlank(message = "用户名不能为空")
            @RequestParam String username,
            @Parameter(description = "新密码", required = true)
            @NotBlank(message = "新密码不能为空")
            @RequestParam String newPassword,
            HttpServletRequest request) {
        try {
            String token = extractToken(request);
            if (token == null) {
                return Result.<Void>error(ResultCode.TOKEN_INVALID, "令牌不存在");
            }

            if (!authService.validateToken(token)) {
                return Result.<Void>error(ResultCode.TOKEN_INVALID, "令牌无效");
            }

            String userType = jwtUtil.getUserTypeFromToken(token);
            if (!"admin".equals(userType)) {
                return Result.<Void>error(ResultCode.PERMISSION_DENIED, "权限不足");
            }
            
            boolean success = authService.resetPassword(username, newPassword);
            
            if (success) {
                logger.info("管理员重置用户密码成功: {}", username);
                return Result.successVoid("密码重置成功");
            } else {
                return Result.<Void>error(ResultCode.ERROR, "密码重置失败");
            }
        } catch (BusinessException e) {
            logger.warn("密码重置失败: {}", e.getMessage());
            return Result.<Void>error(e.getResultCode());
        } catch (Exception e) {
            logger.error("密码重置异常: {}", e.getMessage(), e);
            return Result.<Void>error(ResultCode.SYSTEM_ERROR, "密码重置失败，请稍后重试");
        }
    }

    /**
     * 锁定用户（管理员功能）
     */
    @Operation(summary = "锁定用户", description = "管理员锁定用户账户")
    @PutMapping("/lock/{userId}")
    public Result<Void> lockUser(
            @Parameter(description = "用户ID", required = true)
            @PathVariable String userId,
            HttpServletRequest request) {
        try {
            String token = extractToken(request);
            if (token == null) {
                return Result.<Void>error(ResultCode.TOKEN_INVALID, "令牌不存在");
            }

            if (!authService.validateToken(token)) {
                return Result.<Void>error(ResultCode.TOKEN_INVALID, "令牌无效");
            }

            String userType = jwtUtil.getUserTypeFromToken(token);
            if (!"admin".equals(userType)) {
                return Result.<Void>error(ResultCode.PERMISSION_DENIED, "权限不足");
            }
            
            boolean success = authService.lockUser(userId);
            
            if (success) {
                logger.info("管理员锁定用户成功: {}", userId);
                return Result.successVoid("用户锁定成功");
            } else {
                return Result.<Void>error(ResultCode.ERROR, "用户锁定失败");
            }
        } catch (Exception e) {
            logger.error("用户锁定异常: {}", e.getMessage(), e);
            return Result.<Void>error(ResultCode.SYSTEM_ERROR, "用户锁定失败，请稍后重试");
        }
    }

    /**
     * 解锁用户（管理员功能）
     */
    @Operation(summary = "解锁用户", description = "管理员解锁用户账户")
    @PutMapping("/unlock/{userId}")
    public Result<Void> unlockUser(
            @Parameter(description = "用户ID", required = true)
            @PathVariable String userId,
            HttpServletRequest request) {
        try {
            String token = extractToken(request);
            if (token == null) {
                return Result.<Void>error(ResultCode.TOKEN_INVALID, "令牌不存在");
            }

            if (!authService.validateToken(token)) {
                return Result.<Void>error(ResultCode.TOKEN_INVALID, "令牌无效");
            }

            String userType = jwtUtil.getUserTypeFromToken(token);
            if (!"admin".equals(userType)) {
                return Result.<Void>error(ResultCode.PERMISSION_DENIED, "权限不足");
            }
            
            boolean success = authService.unlockUser(userId);
            
            if (success) {
                logger.info("管理员解锁用户成功: {}", userId);
                return Result.successVoid("用户解锁成功");
            } else {
                return Result.<Void>error(ResultCode.ERROR, "用户解锁失败");
            }
        } catch (Exception e) {
            logger.error("用户解锁异常: {}", e.getMessage(), e);
            return Result.<Void>error(ResultCode.SYSTEM_ERROR, "用户解锁失败，请稍后重试");
        }
    }

    /**
     * 从请求中提取JWT令牌
     */
    private String extractToken(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
}